package com.flyqiu.common.sc;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.flyqiu.common.clazz.FlyQiuFieldFilter;
import com.flyqiu.common.property.FlyQiuPropertyTools;
import com.flyqiu.common.sc.annotations.FlyQiuStatistical;
import com.flyqiu.common.sc.annotations.FlyQiuStatisticalWrapper;
import com.flyqiu.common.sc.calculate.FlyQiuCalculate;
import com.flyqiu.common.sc.statistical.FlyQiuPropertyStatistical;
import com.flyqiu.common.sc.statistical.FlyQiuStatisticalCall;
import com.flyqiu.common.tools.FlyQiuCollectionTools;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * FlyQiySCManage接口提供了数据源管理和统计的功能
 * 它允许注册和创建计算对象，创建上下文，以及对数据源进行统计
 */
public interface FlyQiySCManage {

    String SC_KEY = "_statistical_key";

    /**
     * 注册一个计算类型和对应的计算类
     *
     * @param calculateType  计算类型的字符串标识
     * @param calculateClazz 计算类的Class对象
     */
    <T extends FlyQiuCalculate<?>> void register(String calculateType, Class<T> calculateClazz);

    /**
     * 根据计算类型创建一个计算对象
     *
     * @param calculateType 计算类型的字符串标识
     * @return 对应计算类型的FlyQiuCalculate对象
     */
    FlyQiuCalculate<?> createCalculate(String calculateType);
    /**
     * 匹配具有统计注解的字段
     */
    FlyQiuFieldFilter matchStatisticalAnnotation = (field, clazz) -> {
        FlyQiuStatisticalWrapper annotation = field.getAnnotation(FlyQiuStatisticalWrapper.class);
        FlyQiuStatistical statistical = field.getAnnotation(FlyQiuStatistical.class);
        if (annotation != null && annotation.value().length > 1) {
            return true;
        }
        return statistical != null && !StringUtils.isBlank(statistical.calculateType());
    };

    /**
     * 加载具有统计注解的属性
     *
     * @param clazz 类型Class对象
     * @param <T>   类型泛型
     * @return 统计属性列表
     */
    default <T> List<FlyQiuPropertyStatistical<T>> loadStatisticalProperties(Class<T> clazz) {
        List<List<FlyQiuPropertyStatistical<T>>> properties = FlyQiuPropertyTools.getProperties(clazz, matchStatisticalAnnotation, field -> {
            FlyQiuStatisticalWrapper annotation = field.getAnnotation(FlyQiuStatisticalWrapper.class);
            FlyQiuStatistical statistical = field.getAnnotation(FlyQiuStatistical.class);
            List<FlyQiuPropertyStatistical<T>> cacheList = new ArrayList<>();
            if (annotation != null && annotation.value().length > 1) {
                FlyQiuStatistical[] value = annotation.value();
                for (FlyQiuStatistical flyQiuStatistical : value) {
                    FlyQiuCalculate<?> calculate = createCalculate(flyQiuStatistical.calculateType());
                    cacheList.add(new FlyQiuPropertyStatistical<>(field, flyQiuStatistical.outFieldName(), calculate));
                }
                return cacheList;
            }
            cacheList.add(new FlyQiuPropertyStatistical<>(field, statistical.outFieldName(), createCalculate(statistical.calculateType())));
            return cacheList;
        });
        return properties.stream().flatMap(Collection::stream).toList();
    }

    /**
     * 创建一个默认数据源的FlyQiuSCContext上下文对象
     *
     * @param dataSource 数据源集合
     * @param <T>        数据源的类型
     * @return 创建的FlyQiuSCContext对象
     */
    default <T> FlyQiuSCContext<T> createContext(Collection<T> dataSource) {
        return createContext(dataSource, null);
    }

    /**
     * 创建一个带有特定键和数据源集合的FlyQiuSCContext上下文对象
     *
     * @param <T>        数据源的类型
     * @param dataSource 数据源集合
     * @param key        上下文键，可以为空
     * @return 创建的FlyQiuSCContext对象
     */
    <T> FlyQiuSCContext<T> createContext(Collection<T> dataSource, Object key);

    /**
     * 创建一个默认键和JSON数组数据源的FlyQiuSCContext上下文对象
     *
     * @param dataSource JSON数组数据源
     * @return 创建的FlyQiuSCContext对象
     */
    default FlyQiuSCContext<JSONObject> createContext(JSONArray dataSource) {
        return createContext(dataSource, null);
    }

    /**
     * 创建一个带有特定键和JSON数组数据源的FlyQiuSCContext上下文对象
     *
     * @param dataSource JSON数组数据源
     * @param key        上下文键，可以为空
     * @return 创建的FlyQiuSCContext对象
     */
    FlyQiuSCContext<JSONObject> createContext(JSONArray dataSource, Object key);

    /**
     * 对给定的数据源列表进行统计
     *
     * @param <T>        数据源的类型
     * @param dataSource 数据源列表
     * @param rules      统计规则列表
     * @return 统计结果的JSONObject
     */
    default <T> JSONObject statistical(List<T> dataSource, List<FlyQiuPropertyStatistical<T>> rules) {
        FlyQiuSCContext<T> context = createContext(dataSource);
        return statistical(context, rules);
    }

    /**
     * 根据给定的键值映射函数对数据源进行分组并统计
     *
     * @param <T>        数据源的类型
     * @param <K>        键的类型
     * @param dataSource 数据源列表
     * @param rules      统计规则列表
     * @param keyMapper  数据源到键的映射函数
     * @return 分组统计结果的Map，键为分组键，值为统计结果的JSONObject
     */
    default <T, K> Map<K, JSONObject> groupStatistical(List<T> dataSource, List<FlyQiuPropertyStatistical<T>> rules, Function<T, K> keyMapper) {
        FlyQiuStatisticalCall<K, JSONObject, T, JSONObject> valMapper = (context, statisticalResult, key) -> statisticalResult;
        return groupStatistical(dataSource, rules, keyMapper, valMapper);
    }

    /**
     * 根据给定的键值映射函数和结果映射函数对数据源进行分组并统计
     *
     * @param <T>         数据源的类型
     * @param <K>         键的类型
     * @param <R>         最终结果的类型
     * @param dataSource  数据源列表
     * @param rules       统计规则列表
     * @param keyMapper   数据源到键的映射函数
     * @param valueMapper 统计结果到最终值的映射函数
     * @return 分组统计结果的Map，键为分组键，值为最终结果
     */
    default <T, K, R> Map<K, R> groupStatistical(List<T> dataSource, List<FlyQiuPropertyStatistical<T>> rules, Function<T, K> keyMapper, FlyQiuStatisticalCall<K, JSONObject, T, R> valueMapper) {

        Map<K, List<T>> dataSourceMap = dataSource.stream().collect(Collectors.groupingBy(keyMapper));

        HashMap<K, R> result = new LinkedHashMap<>(dataSourceMap.size());
        FlyQiuSCContext<T> context;
        R apply;
        JSONObject statistical;
        for (Map.Entry<K, List<T>> entry : dataSourceMap.entrySet()) {
            context = createContext(entry.getValue(), entry.getKey());
            statistical = statistical(context, rules);
            statistical.put(SC_KEY, entry.getKey());
            apply = valueMapper.callBack(context, statistical, entry.getKey());
            result.put(entry.getKey(), apply);
        }
        return result;
    }

    /**
     * 对给定的分组数据源进行统计
     *
     * @param <T>           数据源的类型
     * @param <K>           分组键的类型
     * @param dataSourceMap 分组数据源的Map，键为分组键，值为数据源列表
     * @param rules         统计规则列表
     * @return 分组统计结果的Map，键为分组键，值为统计结果的JSONObject
     */
    default <T, K> HashMap<K, JSONObject> statistical(Map<K, List<T>> dataSourceMap, List<FlyQiuPropertyStatistical<T>> rules) {
        if (FlyQiuCollectionTools.isEmpty(dataSourceMap)) {
            return new HashMap<>();
        }
        if (FlyQiuCollectionTools.isEmpty(rules)) {
            return new HashMap<>();
        }
        FlyQiuSCContext<T> context;
        HashMap<K, JSONObject> result = new HashMap<>(dataSourceMap.size());
        for (Map.Entry<K, List<T>> entry : dataSourceMap.entrySet()) {
            context = createContext(entry.getValue(), entry.getKey());
            JSONObject srcRowStatistical = statistical(context, rules);
            result.put(entry.getKey(), srcRowStatistical);
        }
        return result;
    }

    /**
     * 对给定的分组数据源进行统计，并通过回调函数处理统计结果
     *
     * @param <T>           数据源的类型
     * @param <K>           分组键的类型
     * @param <R>           最终结果的类型
     * @param dataSourceMap 分组数据源的Map，键为分组键，值为数据源列表
     * @param rules         统计规则列表
     * @param call          回调函数，处理每个分组的统计结果
     * @return 分组统计结果的Map，键为分组键，值为最终结果
     */
    default <T, K, R> HashMap<K, R> statistical(Map<K, List<T>> dataSourceMap, List<FlyQiuPropertyStatistical<T>> rules, FlyQiuStatisticalCall<K, JSONObject, T, R> call) {
        if (FlyQiuCollectionTools.isEmpty(dataSourceMap)) {
            return new HashMap<>();
        }
        if (FlyQiuCollectionTools.isEmpty(rules)) {
            return new HashMap<>();
        }
        FlyQiuSCContext<T> context;
        HashMap<K, R> result = new HashMap<>(dataSourceMap.size());
        for (Map.Entry<K, List<T>> entry : dataSourceMap.entrySet()) {
            context = createContext(entry.getValue(), entry.getKey());
            JSONObject srcRowStatistical = statistical(context, rules);
            R r = call.callBack(context, srcRowStatistical, entry.getKey());
            result.put(entry.getKey(), r);
        }
        return result;
    }

    /**
     * 在给定的上下文中，根据统计规则对数据源进行统计
     *
     * @param context 上下文对象，包含数据源和可能的其他上下文信息
     * @param rules   统计规则列表
     * @param <T>     数据源的类型
     * @return 统计结果的JSONObject
     */
    <T> JSONObject statistical(FlyQiuSCContext<? extends T> context, List<FlyQiuPropertyStatistical<T>> rules);

}
